package paint;


import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.AbstractList.*;
import java.util.ArrayList;
import java.io.Serializable;


import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
//enum Forme { POINT, SEGMENT }



/**
 * La classe Windows est la fenetre de l'application
 * @author Romain Bustos
 * @author Matthieu Perrin
 * @version 1.0
 */
public class Window extends JFrame 
{
    MonPanel conteneur;
    MenuBar menubar ;
    JTabbedPane tabbedPane;

    /**
    *<b>Constructeur par defaut de la classe Windows</b><br>
    * <p>
    * Est utilisé pour creer un premier onglet contenant un desssin vide <br>
    * </p>
    */
    Window()
    {

        super("Projet PAINT - RIE06");
        setSize(1024, 800);
        setMinimumSize(new Dimension(800, 600));
        setMaximumSize(new Dimension(1024, 1400));
        setVisible(true);
        setEnabled(true);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 

        tabbedPane = new JTabbedPane();

        conteneur = new MonPanel();

        menubar = new MenuBar(this);
        this.setJMenuBar(menubar);

        tabbedPane.addTab("Dessin sans titre", conteneur);
        this.add(tabbedPane);

        validate();
        repaint();
    }
}

/**
 * La classe MonPanel est le contenue d'un onglet (dessin + palette)
 * @author Romain Bustos
 * @author Matthieu Perrin
 * @version 1.0
 */
class MonPanel extends JPanel
{
    ArrayList<Forme> liste_formes = new ArrayList<Forme>();
    CarreSelection selection = new CarreSelection(); // Agregat entre le menu et le dessin (permet de passer la forme et la couleur selectionner)   
    Menu menu = new Menu(selection);
    Dessin dessin;


    /**
    *<b>Constructeur par defaut de la classe MonPanel</b><br>
    * <p>
    * Est utilisé lors de la creation d'un onglet <br>
    * </p>
    */
    MonPanel()
    {
        dessin = new Dessin(selection, liste_formes);

        setLayout(new BorderLayout());
        
        add(menu, BorderLayout.NORTH);
        add(dessin);
    }
      
     
}


/**
 * La classe MenuBar correspond a la bar de menu de l'application
 * @author Romain Bustos
 * @author Matthieu Perrin
 * @version 1.0
 */
class MenuBar extends JMenuBar
{
    MonPanel conteneur;
    Window window;

	private JMenu MenuFichier = new JMenu("Fichier");
	private JMenu MenuEdition = new JMenu("Edition");

	private JMenuItem SousMenuNew = new JMenuItem("Nouveau");
	private JMenuItem SousMenuOuvrir = new JMenuItem("Ouvrir");
	private JMenuItem SousMenuEnregistrer = new JMenuItem("Enregistrer");
	private JMenuItem SousMenuFermerTab = new JMenuItem("Fermer l'onglet");
	private JMenuItem SousMenuFermer = new JMenuItem("Quitter");

	private JMenuItem SousMenuEffacer = new JMenuItem("Tout effacer...");
	private JMenuItem SousMenuSelection = new JMenuItem("Mode selection");

    /**
    *<b>Constructeur par defaut de la classe MenuBar</b><br>
    * <p>
    * Est utilisé lors d'une construction d'une fenetre <br>
    * w = new Window() <br>
    * </p>
    */
        public MenuBar(Window w)
        {
            this.window = w;

            SousMenuFermer.addActionListener(new ActionListener()
                {
			public void actionPerformed(ActionEvent arg0) {
				System.exit(0);
			}
		});

            SousMenuFermerTab.addActionListener(new ActionListener()
                {
			public void actionPerformed(ActionEvent arg0) {
                                        window.tabbedPane.remove(((MonPanel)window.tabbedPane.getSelectedComponent()));
			}
		});

            SousMenuNew.addActionListener(new ActionListener()
                {
			public void actionPerformed(ActionEvent arg0) {
                            MonPanel conteneur = new MonPanel();
                            window.tabbedPane.addTab("Dessin sans titre", conteneur);
                            window.tabbedPane.setSelectedComponent(window.tabbedPane.getComponentAt(window.tabbedPane.getTabCount()-1));
                            window.repaint();
			}
		});

            SousMenuEnregistrer.addActionListener(new ActionListener()
                {
			public void actionPerformed(ActionEvent arg0)
                        {
                        
                            JFileChooser fc = new JFileChooser();
                            
                            if ( fc.showSaveDialog(((MonPanel)window.tabbedPane.getSelectedComponent())) == JFileChooser.APPROVE_OPTION)
                            {
                            ObjectOutputStream oos;
                            try {
                                oos = new ObjectOutputStream( new BufferedOutputStream( new FileOutputStream( new File(fc.getSelectedFile().getPath()))));
                                oos.writeObject(new Sauvegarde(((MonPanel)window.tabbedPane.getSelectedComponent()).dessin.formes));
                                oos.close();
                                window.tabbedPane.setTitleAt(window.tabbedPane.getSelectedIndex(), fc.getSelectedFile().getName());

                            } catch (IOException e)
                            { e.printStackTrace();       }
                            }
                        }
		});

            SousMenuOuvrir.addActionListener(new ActionListener()
                {
			public void actionPerformed(ActionEvent arg0)
                        {

                            JFileChooser fc = new JFileChooser();
                            if ( fc.showOpenDialog(((MonPanel)window.tabbedPane.getSelectedComponent())) == JFileChooser.APPROVE_OPTION)
                            {

                            ObjectInputStream ois;
                            try {
                                ois = new ObjectInputStream( new BufferedInputStream( new FileInputStream( new File(fc.getSelectedFile().getPath()))));
                                try {
                                    MonPanel conteneur = new MonPanel();
                                    conteneur.dessin.formes = ((Sauvegarde)ois.readObject()).getListeForme();
                                    window.tabbedPane.addTab(fc.getSelectedFile().getName() , conteneur);
                                    window.tabbedPane.setSelectedComponent(window.tabbedPane.getComponentAt(window.tabbedPane.getTabCount()-1));
                                    window.tabbedPane.repaint();

                                } catch (ClassNotFoundException e)
                                { e.printStackTrace(); }

                                ois.close();
                            } catch (IOException e)
                            { e.printStackTrace();       }

                            }
                        }
		});

            SousMenuEffacer.addActionListener(new ActionListener()
                {
			public void actionPerformed(ActionEvent arg0) {

                                JOptionPane confirmation = new JOptionPane();
                                int option = confirmation.showConfirmDialog(null, "L'ensemble des formes du dessin seront supprimer. Continuer ?", "Tout effaser", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);

                                if(option == JOptionPane.YES_OPTION)
                                    {
                                    ((MonPanel)window.tabbedPane.getSelectedComponent()).dessin.formes.clear();
                                    ((MonPanel)window.tabbedPane.getSelectedComponent()).dessin.repaint();
                                    }
			}
		});

            SousMenuSelection.addActionListener(new ActionListener()
                {
			public void actionPerformed(ActionEvent arg0)
                        {
                            if ( !((MonPanel)window.tabbedPane.getSelectedComponent()).dessin.getSelection() )
                            {
                                    ((MonPanel)window.tabbedPane.getSelectedComponent()).dessin.setSelection(true);
                                    for ( int i=0; i < window.tabbedPane.getTabCount(); i++)
                                        ((MonPanel)window.tabbedPane.getComponent(i)).menu.setVisible(false);
                                    SousMenuSelection.setText("mode creation");
                            }
                            else
                            {
                                    ((MonPanel)window.tabbedPane.getSelectedComponent()).dessin.setSelection(false);
                                    for ( int i=0; i < window.tabbedPane.getTabCount(); i++)
                                        ((MonPanel)window.tabbedPane.getComponent(i)).menu.setVisible(true);
                                    SousMenuSelection.setText("mode edition");
                            }
			}
		});


            this.MenuFichier.add(SousMenuNew);
            this.MenuFichier.add(SousMenuOuvrir);
            this.MenuFichier.add(SousMenuEnregistrer);
            this.MenuFichier.add(SousMenuFermerTab);
            this.MenuFichier.add(SousMenuFermer);

            this.MenuEdition.add(SousMenuEffacer);
            this.MenuEdition.add(SousMenuSelection);

            this.add(MenuFichier);
            this.add(MenuEdition);

        }
}


/**
 * La classe Sauvegarde sert à sauvegarder le dessin
 * @author Romain Bustos
 * @author Matthieu Perrin
 * @version 1.0
 */
class Sauvegarde implements Serializable
{
    ArrayList<Forme> liste_formes;

    /**
    *<b>Constructeur canonique de la classe Sauvegarde</b><br>
    * <p>
    * Est utilisé lors de la sauvegarde du dessin<br>
    * </p>
    * @param lf
    *   ArrayList<Forme> lf listes des formes du dessin
    */
    public Sauvegarde(ArrayList<Forme> lf)
    {
        this.liste_formes = lf;
    }

    public ArrayList<Forme> getListeForme()
    {
        return liste_formes;
    }
    
}


/**
 * La classe Dessin correspond a la zone de dessin de l'onglet
 * @author Romain Bustos
 * @author Matthieu Perrin
 * @version 1.0
 */
class Dessin extends JPanel
{

    public ArrayList<Forme> formes;
    CarreSelection selection;
    ControlerDessin controleur;

    public static boolean mode_selection = false;


        int x2 =0;
        int y2 =0;
        
        boolean attente = false;

    /**
    *<b>Constructeur de la classe Dessin</b><br>
    * <p>
    * Est utilisé lors de la construction d'un Dessin<br>
    * </p>
    * @param selection
    *   CarreSelection selection contient la forme et la couleur choisie
    * @param formes
    *   ArrayList<Forme> formes contient la liste des formes du dessin
    */
    public Dessin(CarreSelection selection, ArrayList<Forme> formes)
    {
        setLayout(new FlowLayout());

        
        setSize(700, 600);
        setBackground(Color.WHITE);
        setEnabled(true);
        this.formes = formes;
        this.selection = selection;

        controleur = new ControlerDessin(this);

        this.addMouseListener(controleur);

        validate();
    }


    /**
    *<b>Est appelé lors d'un repaint</b><br>
    * <p>
    * Redessine la zone du dessin<br>
    * </p>
    */
    @Override
    public void paintComponent(Graphics g)
    {        
        super.paintComponent(g);

        // Dessine la grille de fond
        g.setColor(Color.LIGHT_GRAY);
        for ( int i = 0; i <= (this.getHeight()/20); i++)
            g.drawLine(0, 20*i, this.getWidth(), 20*i);
        for ( int i = 0; i <= (this.getWidth()/20); i++)
            g.drawLine(20*i, 0, 20*i, this.getHeight());
        
        for ( int i=0; i<formes.size(); i++)
        {
            // Dessine chaque forme
            g.setColor(formes.get(i).getCouleur());
           
            
             if ( formes.get(i) instanceof RectanglePlein)
            {           
                g.fillRect( ((RectanglePlein)formes.get(i)).getOrigine().getX(), ((RectanglePlein)formes.get(i)).getOrigine().getY(), (int)((RectanglePlein)(formes.get(i))).getLarg(), (int)((RectanglePlein)formes.get(i)).getLong());
            }

            if ( formes.get(i) instanceof RectangleVide)
            {
                g.drawRect( ((RectangleVide)formes.get(i)).getOrigine().getX(), ((RectangleVide)formes.get(i)).getOrigine().getY(), ((RectangleVide)(formes.get(i))).getLarg(), ((RectangleVide)formes.get(i)).getLong());                
            }

            if ( formes.get(i) instanceof RoundRectanglePlein )
            {
                g.drawRoundRect(((RoundRectanglePlein)formes.get(i)).getOrigine().getX(), ((RoundRectanglePlein)formes.get(i)).getOrigine().getY(), (int)((RoundRectanglePlein)(formes.get(i))).getLarg(), (int)((RoundRectanglePlein)formes.get(i)).getLong(), (int)((RoundRectanglePlein)formes.get(i)).getArcLarg(), (int)((RoundRectanglePlein)formes.get(i)).getArcLong());
            }

            if ( formes.get(i) instanceof RoundRectangleVide )
            {
                g.fillRoundRect(((RoundRectangleVide)formes.get(i)).getOrigine().getX(), ((RoundRectangleVide)formes.get(i)).getOrigine().getY(), (int)((RoundRectangleVide)(formes.get(i))).getLarg(), (int)((RoundRectangleVide)formes.get(i)).getLong(), (int)((RoundRectangleVide)formes.get(i)).getArcLarg(), (int)((RoundRectangleVide)formes.get(i)).getArcLong());
            }

            if ( formes.get(i) instanceof OvalePlein)
            {      
                g.fillOval( ((OvalePlein)formes.get(i)).getOrigine().getX(), ((OvalePlein)formes.get(i)).getOrigine().getY(), ((OvalePlein)formes.get(i)).getLarg(), ((OvalePlein)formes.get(i)).getLong());
            }
            
            if ( formes.get(i) instanceof OvaleVide)
            {       
                g.drawOval( ((OvaleVide)formes.get(i)).getOrigine().getX(), ((OvaleVide)formes.get(i)).getOrigine().getY(), ((OvaleVide)formes.get(i)).getLarg(), ((OvaleVide)formes.get(i)).getLong());                            
            }           
            
            if ( formes.get(i) instanceof Point)
            {                
                g.drawLine( ((Point)formes.get(i)).getX(), ((Point)formes.get(i)).getY(), ((Point)formes.get(i)).getX(), ((Point)formes.get(i)).getY());
            }            
            
            if ( formes.get(i) instanceof Segment)
            {                
                g.drawLine( ((Segment)formes.get(i)).getA().getX(), ((Segment)formes.get(i)).getA().getY(), ((Segment)formes.get(i)).getB().getX(), ((Segment)formes.get(i)).getB().getY());
            }            
            
        }


        if ( mode_selection )
        {
            g.setColor(Color.BLUE);
            for(int j = 0; j < formes.size(); j++)
                if(formes.get(j).getSelect() == true){
                     if ( formes.get(j) instanceof Rectangle){           
                            g.drawRect( ((Rectangle)formes.get(j)).getOrigine().getX() -5, ((Rectangle)formes.get(j)).getOrigine().getY() -5, (int)((Rectangle)(formes.get(j))).getLarg()+10, (int)((Rectangle)formes.get(j)).getLong()+10);
                     }
                     if ( formes.get(j) instanceof Ovale){           
                            g.drawRect( ((Ovale)formes.get(j)).getOrigine().getX() -5, ((Ovale)formes.get(j)).getOrigine().getY() -5, (int)((Ovale)(formes.get(j))).getLarg()+10, (int)((Ovale)formes.get(j)).getLong()+10);
                     }
                }               
        }

    }

    public void setSelection(boolean mode)
    {
        Dessin.mode_selection = mode;
    }
    public boolean getSelection()
    {
        return Dessin.mode_selection;
    }
    
}



/**
 * La classe Menu correspond a la palette de couleur, la palette de forme et l'affichage  de la selection
 * @author Romain Bustos
 * @author Matthieu Perrin
 * @version 1.0
 */
class Menu extends JPanel
{

    /**
    *<b>Constructeur de la classe Menu</b><br>
    * <p>
    * Est utilisé pour créer les palettes de selections<br>
    * </p>
    */
    Menu(CarreSelection selection)
    {
        setAlignmentX(0);
        setSize(300, 50);
        setLayout(new GridBagLayout());
        GridBagConstraints gbc = new GridBagConstraints();
        
        gbc.gridheight = 1;
        gbc.gridwidth = 1;
        
        //CarreSelection selection = new CarreSelection();
      
        gbc.gridx = 0;
        gbc.gridy = 0;
        
        
        
        ArrayList<Color> liste_couleur = new ArrayList<Color>();
        liste_couleur.add(Color.red);
        liste_couleur.add(Color.ORANGE);
        liste_couleur.add(Color.BLUE);
        liste_couleur.add(Color.green);
        liste_couleur.add(Color.yellow);
        liste_couleur.add(Color.CYAN);
        liste_couleur.add(Color.MAGENTA);
        liste_couleur.add(Color.PINK);
        liste_couleur.add(Color.WHITE);
        liste_couleur.add(Color.black);
        liste_couleur.add(Color.GRAY);
        liste_couleur.add(Color.DARK_GRAY);
        liste_couleur.add(Color.LIGHT_GRAY);
        liste_couleur.add(Color.BLACK);
        
        CarrePalette [] palette = new CarrePalette[liste_couleur.size()];
        
        // Ajout des carre de couleur a la palette
        for ( int i=0; i<palette.length; i++)
        {
            palette[i] = new CarrePalette(liste_couleur.get(i), selection);
        }
        
        // Affiche les carre sur deux lignes
        for ( CarrePalette carre : palette)
        {
            add(carre, gbc);
            gbc.gridx++;
            //if ( gbc.gridx >= (palette.length/2)  )
            if ( gbc.gridx >= (liste_couleur.size()/2)  )
                    {
                        gbc.gridx=0;
                        gbc.gridy++;
                    }
            
        }
        
        
        
        
        
        gbc.gridx = (liste_couleur.size()/2);
        gbc.gridy = 0;
        
        CarreIcone [] icones = new CarreIcone[8];
        
        // Ajoute les icones Ã  la liste des icones
        for (int i =0; i<icones.length; i++)
        {
            icones[i] = new CarreIcone(i+1, selection);        
        }
        
        // Affiches les icones
        for ( CarreIcone icone : icones)
        {
            add(icone, gbc);
            gbc.gridx++;
            
            if ( gbc.gridx-(liste_couleur.size()/2) >= (icones.length/2)  )
                    {
                        gbc.gridx=(liste_couleur.size()/2);
                        gbc.gridy++;
                    }
        }
                   
        gbc.gridx+=(icones.length/2);      
        gbc.gridy=0;
        gbc.gridheight = 2;
        gbc.gridwidth = 2; 
        add(selection, gbc);
        
        
    }


}


/**
 * La classe CarrePalette est utilisé pour chaque carre de la palette de couleur
 * @author Romain Bustos
 * @author Matthieu Perrin
 * @version 1.0
 */
class CarrePalette extends JPanel 
{
    Color couleur;
    CarreSelection selection;
    ControlerCarrePalette controleur;
    
    /**
    *<b>Constructeur de la classe CarrePalette</b><br>
    * <p>
    * Est utilisé lors de la construction de la palette<br>
    * </p>
    * @param couleur
    *   Color couleur : couleur du carre
    * @param selection
    *   CarreSelection selection : contient la couleur selectionner
    */
    CarrePalette(Color couleur, CarreSelection selection)
    {
        this.couleur = couleur;
        this.selection = selection;
        setBackground(couleur);
        setPreferredSize(new Dimension(20, 20));

        controleur = new ControlerCarrePalette(this);
        this.addMouseListener(controleur);
    }
}




/**
 * La classe CarreIcone est utilise pour chaque Icone de la palette d'icone
 * @author Romain Bustos
 * @author Matthieu Perrin
 * @version 1.0
 */
class CarreIcone extends JPanel
{
    
    
    int type = 0;
    CarreSelection selection;
    ControlerCarreIcone controleur;

    /**
    *<b>Constructeur de la classe CarreIcone</b><br>
    * <p>
    * Est utilisé lors de la construction de la palette<br>
    * </p>
    * @param type
    *   int type : type de forme
    * @param selection
    *   CarreSelection selection : contient la couleur selectionner
    */
    public CarreIcone(int type, CarreSelection selection) 
    {        
        setPreferredSize(new Dimension(20, 20));
        this.type = type;
        this.selection = selection;

        controleur = new ControlerCarreIcone(this);
        this.addMouseListener(controleur);
    }
    
    
    
    @Override
    public void paintComponent(Graphics g)
    {
        super.paintComponent(g);

        switch ( type )
        {        
            case 1 :
                g.fillRect(2, 2, 16, 16);
                break;
            case 2 :
                g.drawRect(2, 2, 16, 16);
                break;
            case 3 :
                g.fillOval(2, 2, 16, 16);
                break;
            case 4 :
                g.drawOval(2, 2, 16, 16);
                break;
            case 5 :
                g.fillRoundRect(2, 2, 16, 16, 5, 5);
                
                break;
            case 6 :
                g.drawRoundRect(2, 2, 16, 16, 5, 5);
                break;  
            case 7 :
                g.drawLine(2, 2, 18, 18);
                break;   
            case 8 :
                g.drawLine(10, 10, 10, 10);
                break;
        }
    }

}


/**
 * La classe CarreSelection sert à afficher la forme et la couleur choisie
 * @author Romain Bustos
 * @author Matthieu Perrin
 * @version 1.0
 */
class CarreSelection extends JPanel
{
    Color couleur = Color.BLACK;
    int type = 1;
    
    CarreSelection()
    {
        setPreferredSize(new Dimension(40, 40));
        setBackground(Color.WHITE);
    }
    
    @Override
    public void paintComponent(Graphics g)
    {
        super.paintComponent(g);
     
        
        g.setColor(this.couleur);
        switch ( this.type )
        {        
            case 1 :
                g.fillRect(4, 4, 32, 32);
                break;
            case 2 :
                g.drawRect(4, 4, 32, 32);
                break;
            case 3 :
                g.fillOval(4, 4, 32, 32);
                break;
            case 4 :
                g.drawOval(4, 4, 32, 32);
                break;
            case 5 :
                g.fillRoundRect(4, 4, 32, 32, 5, 5);
                break;
            case 6 :
                g.drawRoundRect(4, 4, 32, 32, 5, 5);
                break;  
            case 7 :
                g.drawLine(4, 4, 36, 36);
                break;   
            case 8 :
                g.drawLine(20, 20, 20, 20);
                break;               
        }
    }

    public void setCouleur(Color c)
    {        
        if ( c instanceof Color )
            this.couleur = c; 
        repaint();
    }
    
    public void setType(int t)
    {        
        if ( t >= 0 && t <= 8)
            this.type = t; 
        repaint();
    }
   
}
